﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UFIDA.U9.Base;
using UFIDA.U9.Base.Currency;
using UFIDA.U9.Base.Doc;
using UFIDA.U9.Base.Organization;
using UFIDA.U9.Base.SOB;
using UFIDA.U9.Base.UOM;
using UFIDA.U9.Base.UserRole;
using UFIDA.U9.CBO.FA.FA_AssetOwnerRelation;
using UFIDA.U9.CBO.FI.Tax;
using UFIDA.U9.CBO.HR.Department;
using UFIDA.U9.CBO.HR.Operator;
using UFIDA.U9.CBO.HR.Person;
using UFIDA.U9.CBO.MFG.BOM;
using UFIDA.U9.CBO.MFG.CO;
using UFIDA.U9.CBO.SCM.Bin;
using UFIDA.U9.CBO.SCM.Item;
using UFIDA.U9.CBO.SCM.Node;
using UFIDA.U9.FA.FA_DepreciationBookBE;
using UFIDA.U9.InvTrans.InvTrans;
using UFIDA.U9.Lot;
using UFIDA.U9.SM.DealerSO;
using UFSoft.UBF.Business;
using UFSoft.UBF.Business.Tool;
using UFSoft.UBF.PL;
using UFSoft.UBF.Util.Data;

namespace ufida.u9.ait.openapi.utils
{
    internal class EntityUtils
    {
        private static Dictionary<string,object> cache=new Dictionary<string, object>();
        static bool IsValid = false;

        static EntityUtils()
        {
            IsValid = U9Utils.IsValid();
        }

        public static string GetEntityCode(BusinessEntity item)
        {
            string code = "";
            if (item.Values.Contains("Code"))
            {
                code = item.Values["Code"].ToString();
            }
            return code;
        }

        public static string GetEntityName(BusinessEntity item)
        {
            string name = "";
            if (item.Values.Contains("Name"))
            {
                object obj= item.Values["Name"];
                if (obj != null && obj is MultiLangDataDict)
                {
                    name = ((MultiLangDataDict)obj).ToSimpleString();
                }
                else
                {
                    name= obj.ToString();
                }
            }
            return name;
        }
        /// <summary>
        /// 获取料品主分类
        /// </summary>
        /// <param name="code">分类编码</param>
        /// <returns></returns>
        public static ItemCategory GetMainItemCategory(string code)
        {
            //分类体系-主分类体系 打钩
            string path = $"Code='{code}' and CategorySystem.IsMain=1";
            ItemCategory cat = EntityUtils.GetEntityByPath<ItemCategory>(path);
            return cat;
        }
        /// <summary>
        /// 获取库位
        /// </summary>
        /// <param name="whcode"></param>
        /// <param name="bincode"></param>
        /// <returns></returns>
        public static Bin GetBin(string whcode,string bincode)
        {
            string path = $"Warehouse.Code='{whcode}' and Code='{bincode}' and Org={Context.LoginOrg.ID}";
            Bin bin = EntityUtils.GetEntityByPath<Bin>(path);
            return bin;
        }

        /// <summary>
        /// 获取所有公司组织
        /// </summary>
        /// <returns></returns>
        public static List<Organization> GetAllOrgs()
        {
            List<Organization> retlist = new List<Organization>();
            //条件 组织形态=公司,门户组织=false
            string path = "OrgClassify=1 and IsPortalOrg=0";
            Organization.EntityList orgs = Organization.Finder.FindAll(path);
            foreach(var org in orgs)
            {
                retlist.Add(org);
            }

            return retlist;
        }

        /// <summary>
        /// 依据工作记录获取任职组织
        /// 取最后一条有效组织
        /// </summary>
        /// <param name="employeeArchive">任职记录</param>
        public static Organization GetEmployeeOrg(EmployeeArchive employeeArchive)
        {
            Organization retorg = null;

            if (employeeArchive.Person.EmployeeAssignments != null && employeeArchive.Person.EmployeeAssignments.Count > 0)
            {
                //依据员工任职记录获取任职组织
                foreach (var ass in employeeArchive.Person.EmployeeAssignments)
                {
                    if (ass.IsMain == true)
                    {
                        //多个主任职时,是最后一条覆盖
                        retorg = ass.BusinessOrg;
                    }
                }
            }
            return retorg;
        }

        /// <summary>
        /// 依据组织编码+部门编码获取部门
        /// 在通用GetEntityCode中也会考虑使用Context中的组织进行查询,这里主要是考虑单据中的跨组织问题
        /// </summary>
        /// <param name="OrgCode">组织编码</param>
        /// <param name="dept">部门编码对象</param>
        /// <returns>部门实体</returns>
        public static Department GetDept(string OrgCode, CopyOfCommonArchiveDataDTOData dept)
        {
            if (dept == null || string.IsNullOrEmpty(dept.Code))
                return null;

            string path = $"Org.Code='{OrgCode}' and Code='{dept.Code}'";
            Department deptEntity = GetEntityByPath<Department>(path);
            return deptEntity;
        }
        /// <summary>
        /// 依据税组合获取税率
        /// </summary>
        /// <param name="taxSchedule">税组合</param>
        /// <returns>税率</returns>
        /// <exception cref="Exception"></exception>
        public static decimal GetTaxRate(TaxSchedule taxSchedule)
        {
            if (taxSchedule == null) throw new Exception("获取税率时,taxSchedule为空");
            if (taxSchedule.TaxScheduleTaxs.Count <= 0) return 0;
            return taxSchedule.TaxScheduleTaxs[0].Tax.TaxRate;
        }
        /// <summary>
        /// 获取 折旧账簿期间
        /// 固定资产 使用
        /// </summary>
        /// <param name="date1"></param>
        /// <returns></returns>
        public static DepreciationBookPeriod GetDpPeriod(DateTime date1)
        {
            DepreciationBook book = GetDpBook();
            string path = $"DPBook={book.ID} and FromDate<='{date1.ToString("d")}' and ToDate>='{date1.ToShortDateString()}'";
            DepreciationBookPeriod period = EntityUtils.GetEntityByPath<DepreciationBookPeriod>(path);//建账折旧账簿期间
            return period;
        }
        /// <summary>
        /// 获取折旧账簿
        /// 固定资产 使用
        /// </summary>
        /// <returns></returns>
        public static DepreciationBook GetDpBook()
        {
            SetofBooks sob = getSetofBooks();
            return EntityUtils.GetEntityByPath<DepreciationBook>($"SOB={sob.ID}");
        }
        /// <summary>
        /// 获取 资产货主关系
        /// 固定资产 使用
        /// </summary>
        /// <returns></returns>
        public static AssetOwnerRelation GetAssetOwnerRelation()
        {
            SetofBooks sob = getSetofBooks();
            AssetOwnerRelation rel = EntityUtils.GetEntityByPath<AssetOwnerRelation>($"OwnerOrg={Context.LoginOrg.ID} and OwnerOrgSOB={sob.ID}");
            return rel;
        }

        /// <summary>
        /// 获取批次主档
        /// </summary>
        /// <param name="LotCode">批号</param>
        /// <param name="ItemCode">料号</param>
        /// <returns></returns>
        public static LotMaster GetLot(String LotCode,String ItemCode)
        {
            //N'' 使用unicode编码
            String path = $"LotCode=N'{LotCode}' and ItemCode=N'{ItemCode}'";
            LotMaster lot = LotMaster.Finder.Find(path);
            if (lot == null) throw new Exception($"查询批次主档失败,path:[{path}]");
            return lot;
        }
        /// <summary>
        /// 依据料号和日期查询有效BOM
        /// </summary>
        /// <param name="code">料号</param>
        /// <param name="date">日期</param>
        /// <returns></returns>
        public static BOMMaster GetBOM(String code,DateTime date)
        {
            //N'' 使用unicode编码
            String path = $"ItemMaster.Code=N'{code}' and EffectiveDate<='{date.ToString()}' and DisableDate>='{date.ToString()}'";
            BOMMaster bom=BOMMaster.Finder.Find(path);
            if (bom == null) throw new Exception($"查找BOM失败,path:[{path}]");
            return bom;
        }

        public static decimal GetRoundNum(UOM uom,decimal num)
        {
            //return Math.Round(num, uom.Round.Precision);
            return uom.Round.GetRoundValue(num);
        }

        /// <summary>
        /// 依据币种精度,设置金额精度
        /// </summary>
        /// <param name="curr">币种</param>
        /// <param name="mny">金额</param>
        /// <returns></returns>
        public static decimal GetRoundMny(Currency curr,decimal mny)
        {
            //return Math.Round(mny, curr.MoneyRound.Precision);
            return curr.MoneyRound.GetRoundValue(mny);
        }

        public static decimal GetRoundPrice(Currency curr,decimal price)
        {
            //return Math.Round(price, curr.PriceRound.Precision);
            return curr.PriceRound.GetRoundValue(price);
        }

        /// <summary>
        /// 依据实体ID更新实体单号
        /// </summary>
        /// <typeparam name="T">单据泛型</typeparam>
        /// <param name="id">被更新实体ID</param>
        /// <param name="DocNo">更新成的实体单号</param>
        /// <returns></returns>
        public static string UpdateDocNo<T>(long id, string DocNo) where T : Doc
        {
            if (DocNo.StartsWith("PubDescSeg") || DocNo.StartsWith("PrivateDescSeg"))
            {
                return DocNo;
            }

            Type type = typeof(T);
            //清除缓存
            BusinessEntity.EntityKey key = new BusinessEntity.EntityKey(id, type.FullName);
            U9Utils.ClearCache(key);
            //U9Utils.ClearCache();

            //查询实体
            EntityQuery query = new EntityQuery(type.FullName);
            T entity = (T)query.FindByID(id);
            if (entity.DocNo.Equals(DocNo))
                return DocNo;

            //单号不同时,进行更新
            using (ISession session = Session.Open())
            {
                //更新实体
                entity.DocNo = DocNo;
                session.Commit();
            }
            return entity.DocNo;
        }

        /// <summary>
        /// 获取料品分类
        /// </summary>
        /// <param name="code"></param>
        /// <param name="CategoryKind"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static ItemCategory getItemCategory(String code,int CategoryKind)
        {
            string key = $"getItemCategory-{code}-{CategoryKind}";

            try
            {
                if (cache.ContainsKey(key))
                    return cache[key] as ItemCategory;

                String opath = "";
                //查询料品分类
                switch (CategoryKind)
                {
                    case 0:
                        opath = $"Code={code} and CategorySystem.IsStock=1";
                        break;
                    case 1:
                        opath = $"Code={code} and CategorySystem.IsMFG=1";
                        break;
                    case 2:
                        opath = $"Code={code} and CategorySystem.IsSale=1";

                        break;
                    case 3:
                        opath = $"Code={code} and CategorySystem.IsSale=1";
                        break;
                    case 4:
                        opath = $"Code={code} and CategorySystem.IsMRP=1";
                        break;
                    case 5:
                        opath = $"Code={code} and CategorySystem.IsCost=1";
                        break;
                    case 6:
                        opath = $"Code={code} and CategorySystem.IsCredit=1";
                        break;
                    case 7:
                        opath = $"Code={code} and CategorySystem.IsPrice=1";
                        break;
                    case 8:
                        opath = $"Code={code} and CategorySystem.IsFI=1";
                        break;
                    default:
                        break;
                }
                ItemCategory cate = ItemCategory.Finder.Find(opath);
                if (cate == null)
                    throw new Exception($"查询料品分类失败,oql:{opath}");

                cache[key] = cate;
                return cate;
            }
            catch (Exception ex)
            {
                throw new Exception($"查询料品分类失败,错误消息:{ex.Message}", ex);
            }
        }

        public static CostField getCostField(String whcode)
        {
            string key = $"getCostField-{Context.LoginOrg.ID}-{whcode}";

            try
            {
                if (cache.ContainsKey(key))
                    return cache[key] as CostField;

                string path = $"Org.ID={Context.LoginOrg.ID} and Warehouse.Code='{whcode}'";
                CostFieldObject costFieldObject = CostFieldObject.Finder.Find(path);
                if (costFieldObject == null)
                    throw new Exception($"查找成本域失败,path:[{path}]");
                cache[key] = costFieldObject;
                return costFieldObject.CostField;
            }catch (Exception ex)
            {
                throw new Exception($"查找成本域失败,错误消息:{ex.Message}",ex);
            }
        }
        /// <summary>
        /// 依据日期获取当前组织的账簿会计期间
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static SOBAccountingPeriod getPostPeriod(DateTime date)
        {
            SetofBooks sob = getSetofBooks();
            String path = $"SetofBooks={sob.ID} and AccountPeriod.FromDate<='{date.ToString("d")}' and AccountPeriod.ToDate>='{date.ToShortDateString()}'";
            SOBAccountingPeriod period = SOBAccountingPeriod.Finder.Find(path);
            if (period == null)
                throw new Exception($"查询账簿会计期间失败!path:[{path}]");

            return period;
        }
        /// <summary>
        /// 获取当前组织的账簿
        /// </summary>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static SetofBooks getSetofBooks()
        {
            Type type = typeof(SetofBooks);
            string key = $"{type.FullName}-{Context.LoginOrg.Code}";

            try
            {
                //取缓存
                if (cache.ContainsKey(key))
                {
                    return cache[key] as SetofBooks;
                }
                SetofBooks book = SetofBooks.Finder.Find($"Org.ID={Context.LoginOrg.ID} and SOBType=0");
                if (book == null)
                    throw new Exception($"查找主账簿失败,组织编码:[{Context.LoginOrg.Code}]");
                cache[key] = book;
                return book;
            }catch (Exception ex)
            {
                throw new Exception($"查找主账簿失败,错误消息:{ex.Message}",ex);
            }
        }
        /// <summary>
        /// 判断实体中是否存在某属性
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="prop">属性名称</param>
        /// <returns></returns>
        public static bool HasProperty(Entity entity,string prop)
        {
            try
            {
                entity.GetValue(prop);
                return true;
            }catch
            {
                return false;
            }
        }

        /// <summary>
        /// 通过弱类型查询实体
        /// </summary>
        /// <param name="fullName">实体全名,用.分隔</param>
        /// <param name="code">实体编码</param>
        /// <returns>强类型实体数据</returns>
        /// <exception cref="Exception"></exception>
        public static BusinessEntity GetBusinessEntity(String fullName,string code)
        {
            string path = "";
            Entity entity = BusinessEntity.Create(fullName, null);
            string entityName = ExtendHelpAPI.GetEntityResource(fullName);

            if (HasProperty(entity,"DocNo"))
            {
                path = $"DocNo='{code}'";
            }else if (HasProperty(entity,"Code"))
            {
                path = $"Code='{code}'";
            }
            EntityQuery query = new EntityQuery(fullName);
            System.Collections.IList retlist = query.FindAll(path);
            if (retlist == null || retlist.Count == 0)
            {
                throw new Exception($"查询实体失败,实体名:[{entityName}],实体编码:[{code}]");
            }
            return (BusinessEntity)retlist[0];
        }

        public static T GetDocEntity<T>(string docno) where T : Doc
        {
            Type type = typeof(T);
            string path = "1=1";
            string key = $"{type.FullName}-{Context.LoginOrg.Code}-{docno}";
            string entityName = ExtendHelpAPI.GetEntityResource(type.FullName);
            try
            {
                if (docno == null || docno.Length == 0)
                    return null;

                //依据实体字段拼接where条件
                path = $"DocNo='{docno}' and Org.ID={Context.LoginOrg.ID}";

                //查询数据
                EntityQuery query = new EntityQuery(type.FullName);
                System.Collections.IList list = query.FindAll(path);
                if (list.Count == 0)
                {
                    throw new Exception($"查找数据为空! 实体:[{entityName}],单号:[{docno}]");
                }
                return list[0] as T;
            }
            catch (Exception ex)
            {
                throw new Exception($"查找单据失败! 实体:[{entityName}],单号:[{docno}]", ex);
            }
        }
        /// <summary>
        /// 通过编码查询实体
        /// 查询不到,报错
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="code">实体编码值</param>
        /// <param name="field">实体编码字段</param>
        /// <returns>实体,仅一条</returns>
        public static T GetEntityByCode<T>(String code,String field) where T : BusinessEntity
        {
            if (!IsValid)
            {
                return null;
            }

            Type type = typeof(T);
            string path = "1=1";
            string key = $"{type.FullName}-{Context.LoginOrg.Code}-{field}-{code}";
            System.Reflection.PropertyInfo[] props = type.GetProperties();
            string entityName=ExtendHelpAPI.GetEntityResource(type.FullName);

            try
            {
                if (code == null || code.Length == 0)
                    throw new Exception($"查询[{entityName}]参数为空");

                //取缓存
                //这里不再取缓存,因为数据发生变化后,需要重启U9缓存才能生效
                //if (cache.ContainsKey(key))
                //{
                //    return cache[key] as T;
                //}

                //依据实体字段拼接where条件
                if (!props.Any(item =>
                {
                    return item.Name == field;
                }))
                {
                    throw new Exception($"查询[{entityName}]不存在[{field}]字段");
                }
                //N'' 使用unicode编码
                path += $" and {field}=N'{code}'";
                //如果该实体包含Org字段,用上下文拼接Org查询条件
                if (props.Any(item =>
                {
                    return item.Name == "Org";
                }))
                {
                    path += $" and Org.ID='{Context.LoginOrg.ID}'";
                }

                //查询数据
                EntityQuery query = new EntityQuery(type.FullName);
                System.Collections.IList list = query.FindAll(path);
                if (list.Count == 0)
                {
                    throw new Exception($"查找[{entityName}][{code}]数据为空! 查询条件:[{path}]");
                }
                //设置缓存
                cache[key] = list[0];
                //返回
                return list[0] as T;
            }
            catch (Exception ex)
            {
                throw new Exception($"查找[{entityName}][{code}]失败或者不存在! 查询条件:[{path}]", ex);
            }
        }

        /// <summary>
        /// 依据Code字段翻译为ID字段,如果Code为空,则报错
        /// 这样要求该字段必填
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="code"></param>
        /// <returns></returns>
        public static T GetEntityByCode<T>(string code) where T : BusinessEntity
        {
            return GetEntityByCode<T>(code, "Code");
        }

        public static T GetEntityByCommon<T>(CopyOfCommonArchiveDataDTOData dto) where T : BusinessEntity
        {
            if (dto == null) return null;
            if (dto.ID > 0)
            {
                return GetEntityByID<T>(dto.ID);
            }
            else if (!string.IsNullOrEmpty(dto.Code))
            {
                return GetEntityByCode<T>(dto.Code, "Code");
            }
            return null;
        }

        private static T GetEntityByID<T>(long ID) where T : BusinessEntity
        {
            if (!IsValid)
            {
                return null;
            }

            Type type = typeof(T);
            //string path = "1=1";
            string key = $"{type.FullName}-{Context.LoginOrg.Code}-ID-{ID}";
            System.Reflection.PropertyInfo[] props = type.GetProperties();
            string entityName = ExtendHelpAPI.GetEntityResource(type.FullName);

            try
            {
                if (ID == 0)
                    throw new Exception($"查询实体传入ID为空,实体:[{entityName}]");

                //取缓存
                //这里不再取缓存,因为数据发生变化后,需要重启U9缓存才能生效
                //if (cache.ContainsKey(key))
                //{
                //    return cache[key] as T;
                //}

                //依据实体字段拼接where条件
                if (!props.Any(item =>
                {
                    return item.Name == "ID";
                }))
                {
                    throw new Exception($"实体:[{entityName}]不存在[ID]字段");
                }

                //查询数据
                EntityQuery query = new EntityQuery(type.FullName);
                Entity entity = query.FindByID(ID);
                if (entity==null)
                {
                    throw new Exception($"查找数据失败! 实体:[{entityName}],字段[ID],值:[{ID}]");
                }
                //设置缓存
                cache[key] = entity;
                //返回
                return entity as T;
            }
            catch (Exception ex)
            {
                throw new Exception($"查找实体失败! 实体:[{entityName}],字段[ID],值:[{ID}]", ex);
            }
        }

        /// <summary>
        /// 依据通用档案DTO翻译为ID,如果DTO为空则不翻译
        /// 字段为可填项,而不是必填项
        /// </summary>
        /// <typeparam name="T">档案类型</typeparam>
        /// <param name="dto">通用档案DTO</param>
        /// <returns></returns>
        public static T GetEntityByCode<T>(CopyOfCommonArchiveDataDTOData dto) where T : BusinessEntity
        {
            if (dto == null|| string.IsNullOrEmpty(dto.Code) ||dto.Code.Length==0)
                return default(T);
            return GetEntityByCode<T>(dto.Code, "Code");
        }

        /// <summary>
        /// 依据通用档案DTO翻译为ID,如果DTO为空则不翻译
        /// 通过参数判断字段是否必填
        /// </summary>
        /// <typeparam name="T">档案类型</typeparam>
        /// <param name="dto">通用档案DTO</param>
        /// <param name="isNeed">是否必填</param>
        /// <returns></returns>
        public static T GetEntityByCode<T>(CopyOfCommonArchiveDataDTOData dto,bool isNeed) where T : BusinessEntity
        {
            if (isNeed)
            {
                if (dto == null || string.IsNullOrEmpty(dto.Code) || dto.Code.Length == 0)
                    return GetEntityByCode<T>("", "Code");
                else
                    return GetEntityByCode<T>(dto.Code, "Code");
            }
            else
            {
                return GetEntityByCode<T>(dto);
            }
            
        }

        public static T GetEntityByPath<T>(String path) where T : BusinessEntity
        {
            Type type = typeof(T);
            string entityName = ExtendHelpAPI.GetEntityResource(type.FullName);
            try
            {
                EntityQuery query = new EntityQuery(type.FullName);
                System.Collections.IList list = query.FindAll(path);
                if (list.Count == 0)
                {
                    throw new Exception($"查找不到数据,实体[{entityName}],path:[{path}]");
                }
                return list[0] as T;
            }catch (Exception ex)
            {
                throw new Exception($"查询实体出错,实体:[{entityName}],path:[{path}],错误消息:[{ex.Message}]",ex);
            }
        }
    }
}
